package com.feng.controller;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.feng.config.GetHttpSessionConfig;
import com.feng.entity.ChatRoom;
import com.feng.entity.vo.ChatRoomVo;
import com.feng.service.ChatRoomService;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/chatRoom", configurator = GetHttpSessionConfig.class)
@Component
@Slf4j
public class ChatRoomServer implements ApplicationContextAware {

    private ChatRoomService chatRoomService;

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    // 存储会话ID到Session的映射
    static Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    // 存储用户ID与会话ID的映射（改为userId -> sessionId）
    static Map<String, String> userIdMap = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session) {
        try {
            chatRoomService = context.getBean(ChatRoomService.class);
            String userId = (String) session.getUserProperties().get("userId");

            // 处理用户重复登录：先断开之前的会话
            if (userId != null && userIdMap.containsKey(userId)) {
                String oldSessionId = userIdMap.get(userId);
                Session oldSession = sessionMap.get(oldSessionId);
                if (oldSession != null && oldSession.isOpen()) {
                    try {
                        oldSession.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "重复登录"));
                        log.info("用户 {} 的旧会话 {} 已关闭", userId, oldSessionId);
                    } catch (Exception e) {
                        log.error("关闭旧会话失败", e);
                    }
                }
                userIdMap.remove(userId);
                sessionMap.remove(oldSessionId);
            }
            // 建立新会话
            sessionMap.put(session.getId(), session);
            if (userId != null) {
                userIdMap.put(userId, session.getId());
            }
            // 更新在线人数并广播
            broadcastUserCount();
            log.info("会话 {} 已连接，用户ID: {}", session.getId(), userId);
        } catch (Exception e) {
            log.error("处理WebSocket连接时发生错误", e);
            // 发生异常时清理会话
            sessionMap.remove(session.getId());
        }
    }

    @OnMessage
    public void onMessage(String message) {
        try {
            ChatRoom chatRoom = JSONUtil.toBean(message, ChatRoom.class);
            chatRoom.setTime(new Date());
            chatRoomService.save(chatRoom);

            ChatRoomVo chatRoomVo = new ChatRoomVo();
            chatRoomVo.setChatRoom(chatRoom);
            chatRoomVo.setUserCount(sessionMap.size());
            chatRoomVo.setType("message");

            String mes = JSONObject.toJSONString(chatRoomVo);
            sendAllMessage(mes);

            log.info("收到消息：{}", mes);
        } catch (Exception e) {
            log.error("处理WebSocket消息时发生错误", e);
        }
    }

    private void sendAllMessage(String mes) {
        // 创建会话副本避免并发修改问题
        Map<String, Session> copyOfSessionMap = new HashMap<>(sessionMap);

        for (Map.Entry<String, Session> entry : copyOfSessionMap.entrySet()) {
            String sessionId = entry.getKey();
            Session session = entry.getValue();

            try {
                if (session != null && session.isOpen()) {
                    session.getBasicRemote().sendText(mes);
                } else {
                    // 清理无效会话
                    sessionMap.remove(sessionId);
                    String userId = getUserIdBySessionId(sessionId);
                    if (userId != null) {
                        userIdMap.remove(userId);
                    }
                    log.info("会话 {} 已关闭，已从映射中移除", sessionId);
                }
            } catch (Exception e) {
                log.error("向会话 {} 发送消息失败，已移除", sessionId, e);
                sessionMap.remove(sessionId);
                String userId = getUserIdBySessionId(sessionId);
                if (userId != null) {
                    userIdMap.remove(userId);
                }
            }
        }
    }

    @OnClose
    public void onClose(Session session) {
        try {
            String sessionId = session.getId();
            String userId = getUserIdBySessionId(sessionId);
            // 移除会话映射
            sessionMap.remove(sessionId);
            if (userId != null) {
                userIdMap.remove(userId);
            }

            // 更新在线人数并广播
            broadcastUserCount();

            log.info("会话 {} 已关闭，用户ID: {}", sessionId, userId);
        } catch (Exception e) {
            log.error("处理WebSocket关闭时发生错误", e);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        try {
            String sessionId = session.getId();
            String userId = getUserIdBySessionId(sessionId);

            // 清理会话
            sessionMap.remove(sessionId);
            if (userId != null) {
                userIdMap.remove(userId);
            }

            log.error("会话 {} 发生错误，用户ID: {}", sessionId, userId, error);
        } catch (Exception e) {
            log.error("处理WebSocket错误时发生错误", e);
        }
    }

    // 辅助方法：通过会话ID获取用户ID
    private String getUserIdBySessionId(String sessionId) {
        // 反转映射查找（效率较低，考虑改用双向映射）
        for (Map.Entry<String, String> entry : userIdMap.entrySet()) {
            if (entry.getValue().equals(sessionId)) {
                return entry.getKey();
            }
        }
        return null;
    }

    // 广播在线用户数量
    private void broadcastUserCount() {
        ChatRoomVo chatRoomVo = new ChatRoomVo();
        chatRoomVo.setUserCount(sessionMap.size());
        chatRoomVo.setType("userCount");
        String mes = JSONObject.toJSONString(chatRoomVo);
        sendAllMessage(mes);
    }
}